#include "HttpDecoder.h"
#include "HttpHandler.h"
#include <cassert>
#include <cstring>


static int onMessageBegin(http_parser* pParser){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    decoder->m_finish = false;
    decoder->m_cache.method = std::string(http_method_str((http_method)pParser->method));
    return 0;
}
static int onUrl(http_parser* pParser, const char* at, size_t length){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    decoder->m_cache.url = std::string(at, length);
    return 0;
}
static int onStatus(http_parser* pParser, const char* at, size_t length){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    // TODO: record state
    return 0;
}
static int onHeaderField(http_parser* pParser, const char* at, size_t length){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    decoder->last_head_item.first = std::string(at, length);
    return 0;
}
static int onHeaderValue(http_parser* pParser, const char* at, size_t length){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    decoder->last_head_item.second = std::string(at, length);
    decoder->m_cache.heads.emplace(std::move(decoder->last_head_item));
    return 0;
}
static int onHeadersComplete(http_parser* pParser){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    return 0;
}
static int onBody(http_parser* pParser, const char* at, size_t length){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    decoder->m_cache.body = std::string(at, length);
    return 0;
}
static int onMessageComplete(http_parser* pParser){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    decoder->m_finish = true;
    return 0;
}
static int onTrunkHeader(http_parser* pParser){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    // TODO: support trunk mode
    return 0;
}
static int onTrunkComplete(http_parser* pParser){
    HttpDecoder* decoder = static_cast<HttpDecoder*>(pParser->data);
    if (decoder == nullptr) return -1;
    // TODO: support trunk mode
    return 0;
}

static http_parser_settings* GetParserSettting()
{
    static http_parser_settings settings;
    static bool is_init = false;
    if (!is_init)
    {
        http_parser_settings_init(&settings);
        
        settings.on_message_begin 	    = onMessageBegin;
        settings.on_url 		        = onUrl;
        settings.on_status 	            = onStatus;
        settings.on_header_field 		= onHeaderField;
        settings.on_header_value 		= onHeaderValue; 
        settings.on_headers_complete 	= onHeadersComplete; 
        settings.on_body 				= onBody;
        settings.on_message_complete 	= onMessageComplete;
        settings.on_chunk_header 	    = onTrunkHeader; 
        settings.on_chunk_complete 	    = onTrunkComplete;
    }
    return &settings;
}

HttpDecoder::HttpDecoder()
{
    m_offset = 0;
}
HttpDecoder::~HttpDecoder()
{
    m_offset = 0;
}
int HttpDecoder::ParsePacket(char *data, int len, CCSocketHandler *handle, CWspack *wspack)
{
    HttpHandler* pHttpHandler = dynamic_cast<HttpHandler*>(handle);
    assert(pHttpHandler);
    if (m_offset > len)
    {
		GlobalUtils::LogNotice("offset err, handle[" + std::to_string(pHttpHandler->m_HandlerID) + "],offset:" + std::to_string(m_offset) + ",len:" + std::to_string(len));
        return -1;  // 已处理长度 不应该大于总长度
    }
    if (m_offset == 0)
    {
        // 新的解析開始
        http_parser_init(&m_paeser, HTTP_REQUEST);
        m_paeser.data = this;
        m_finish = false;
        pHttpHandler->setDataProtocol(DATA_PROTOCOL_HTTP);  
    }
    /*
        data: 当前未处理的数据包
        len:  当前未处理的总数据长度
        m_offset: 当前已解析的数据长度

        待处理数据为 data + m_offset
        待处理长度为 len - m_offset
    */
    int nparsed = http_parser_execute(&m_paeser, GetParserSettting(), data + m_offset, len - m_offset);
    if (m_paeser.upgrade) 
    {
		GlobalUtils::LogNotice("not support upgrade, handle[" + std::to_string(pHttpHandler->m_HandlerID) + "]");
        return -1;		// 不支持WebSocket
    }
    // else if(nparsed != (size_t)len)
    // {
	// 	log_notice ("nparsed not match, handle[%d],nparsed:%d,len:%d", pHttpHandler->m_HandlerID, nparsed, len);
    //     return -1;  // 解析长度不对
    // }

    m_offset += nparsed;  // 更新当前解析长度

    int total_nparsed = 0;
    // 所有数据处理完成
    if (m_finish) 
    {
        total_nparsed = m_offset;
        // 重置
        m_offset = 0;
    }
    // 当前包还没有正确处理完
    return total_nparsed;
}

void HttpDecoder::Reset()
{
    m_offset = 0;
    m_finish = false;
    m_cache.Reset();
    last_head_item.first.clear();
    last_head_item.second.clear();
}

bool HttpDecoder::IsComplete() const
{
    return m_finish;
}

std::unique_ptr<CCPacketBase> HttpDecoder::GetPacket()
{
    if (m_finish) {
        auto packet = std::unique_ptr<CSimplePacket>(new CSimplePacket(0));
        std::string packet_data = m_cache.ToString();
        packet->SetData(packet_data.data(), packet_data.size());
        return packet;
    }
    return nullptr;
}

static void DumpUrlData(std::string& value, const char* url, struct http_parser_url* u, std::uint16_t index)
{
    if (u->field_set & (1 << index))
    {
        value.assign(url + u->field_data[index].off, u->field_data[index].len);
    }
    else
    {
        if ((index == UF_PORT) && (u->field_set & (1 << UF_SCHEMA)))
        {
            if (4 == u->field_data[UF_SCHEMA].len && !memcmp("http", url + u->field_data[UF_SCHEMA].off, 4))
            {
                value = "80";
            }
            else if (5 == u->field_data[UF_SCHEMA].len && !memcmp("https", url + u->field_data[UF_SCHEMA].off, 4))
            {
                value = "443";
            }
        }
    }
}

static void DumpQuery(stUrlParamVec& params, const std::string& query)
{
    char *s, *e, *c;
    s = c = const_cast<char*>(query.c_str());
    e = s + query.size();
    
    int state = 0;
    while (c != e)
    {
        switch (*c)
        {
        case '=':
        {
            stUrlParam url_param;
            url_param.key.assign(s, c - s);
            params.emplace_back(std::move(url_param));

            state = 1;
            s = c + 1;
            c = s;
        }
            break;
        case '&':
        {
            params.back().val.assign(s, c - s);

            state = 2;
            s = c + 1;
            c = s;
        }
            break;
        default:
            if (state == 2) state = 0;
            c++;
            break;
        }
    }
    switch (state)
    {
    case 0:
    {
        stUrlParam url_param;
        url_param.key.assign(s, c - s);
        if (!url_param.key.empty()) 
        {
            params.emplace_back(std::move(url_param));
        }
    }
        break;
    case 1:
    {
        params.back().val.assign(s, c - s);
    }
    case 2:
    default:
        break;
    }
}
bool HttpDecoder::ParserUrl(stUrlData& st_url_data, const std::string& url)
{
	struct http_parser_url u;
    http_parser_url_init(&u);
	if (0 != http_parser_parse_url(url.c_str(), url.size(), 0, &u))
	{
        return false;
	}

    DumpUrlData(st_url_data.schema, url.c_str(), &u, UF_SCHEMA);
    DumpUrlData(st_url_data.host, url.c_str(), &u, UF_HOST);
    DumpUrlData(st_url_data.port, url.c_str(), &u, UF_PORT);
    DumpUrlData(st_url_data.path, url.c_str(), &u, UF_PATH);
    DumpUrlData(st_url_data.fragment, url.c_str(), &u, UF_FRAGMENT);
    DumpUrlData(st_url_data.userinfo, url.c_str(), &u, UF_USERINFO);
    std::string query;
    DumpUrlData(query, url.c_str(), &u, UF_QUERY);
    DumpQuery(st_url_data.params, query);
    return true;
}



